﻿/*
 *  Copyright (C) 2010 by Benjamin Nolmans <benjamin.nolmans@gmail.com>
 *  and Barbara Post <postb99@gmail.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Mapping.ByCode;
using OsamesFrameworkCore.Configuration;
using OsamesFrameworkCore.DataLayer;
using OsamesFrameworkCore.Logging;
using OsamesFrameworkCore.Plugins;
using OsamesFrameworkCore.Tools;

namespace NHibernatePlugin
{
    public class NHibernatePlugin : AbstractPlugin
    {
        private const string CST_STR_CONFIG_SECTION = "sectionNHibernateConfig";
        private const string CST_STR_DIALECT_NODE = "NHibernateDialect";
        private const string CST_STR_CONNECTION_STRING_NODE = "ConnectionString";

        private const string CST_STR_CONFIG_ERROR = "Configuration error";

        private static ISessionFactory _sessionFactory;
        private static string _strNHibernateDialect;
        private static string _strConnectionString;

        /// <summary>
        /// Initialize.
        /// </summary>
        public override void Initialize()
        {
            ValidateConfiguration();
            InitializeSessionFactory(); 
        }
        /// <summary>
        /// Dispose.
        /// </summary>
        public override void Dispose()
        {
            _sessionFactory.Dispose();
        }

        /// <summary>
        /// Helper method to query for result of some type with condition expressed on this type properties.
        /// </summary>
        /// <typeparam name="T">IDataObject type</typeparam>
        /// <param name="lambdaExpressionCondition_">Lambda expression to describe a condition, for example :
        /// p_ => p_.SomeProperty == "someValue"</param>
        /// <returns></returns>
        public IList<T> QueryConditionOnType<T>(System.Linq.Expressions.Expression<Func<T, bool>> lambdaExpressionCondition_) where T : class
        {
            // TODO : ici ajouter un test que T possède une classe de mapping dans le model mapper (sinon l'oubli ne génère pas d'erreur
            // mais un résultat vide). Voir pour cela du côté de "mapper_.Class<Personne3>" et utiliser une liste pour cacher le résultat 
            // (ne pas vérifier deux fois).
            using (ISession session = _sessionFactory.OpenSession())
            {
                IList<T> lstObjects = session.QueryOver<T>().Where(lambdaExpressionCondition_).List();
                return lstObjects;
            }
        } 

        /// <summary>
        /// Validates that configuration parameters allow to query database.
        /// </summary>
        private void ValidateConfiguration()
        {
            _strNHibernateDialect = XmlDictPluginConfiguration.FindValue(CST_STR_CONFIG_SECTION, CST_STR_DIALECT_NODE);
            if (string.IsNullOrEmpty(_strNHibernateDialect))
            {
                string strError = string.Format("{0}/{1} cannot be empty", CST_STR_CONFIG_SECTION, CST_STR_DIALECT_NODE);
                Exception ex = new Exception(strError);
                Log(LogLevel.Fatal, CST_STR_CONFIG_ERROR, ex);
                throw ex;
            }

            try
            {
                Type tNHibernateDialectType = Type.GetType(_strNHibernateDialect);
            } catch(TypeLoadException ex)
            {
                Log(LogLevel.Fatal, CST_STR_CONFIG_ERROR, ex);
                throw ex;
            }
            _strConnectionString = XmlDictPluginConfiguration.FindValue(CST_STR_CONFIG_SECTION, CST_STR_CONNECTION_STRING_NODE);
            if (string.IsNullOrEmpty(_strConnectionString))
            {
                string strError = string.Format("{0}/{1} cannot be empty", CST_STR_CONFIG_SECTION, CST_STR_CONNECTION_STRING_NODE);
                Exception ex = new Exception(strError);
                Log(LogLevel.Fatal, CST_STR_CONFIG_ERROR, ex);
                throw ex;
            }
        }

        /// <summary>
        /// Initializes session factory.
        /// </summary>
        private void InitializeSessionFactory()
        {
            // Model mapper
            ModelMapper mapper = new ModelMapper();

            // Remplissage du mapper en utilisant IClassMap
            // Remarque : ici il va inspecter toutes les dlls dans le répertoire des plugins. 
            string strLookupPath = CoreConfiguration.PluginsDirectoryPath;

            List<IClassMap> lstIClassMap = ReflectionTools.FindPublicConcreteClassImplementing<IClassMap>(strLookupPath);
            int iCount = lstIClassMap.Count;
            if(iCount > 0)
            {
                for(int i = 0; i < iCount; i++)
                {
                    LoggerService.Log(this, LogLevel.Info, "Class to map : {0}", new object[] {lstIClassMap[i].GetType().FullName});
                    lstIClassMap[i].Map(mapper);
                }
            } else
            {
                LoggerService.Log(this, LogLevel.Warn, "No IClassMap mapping classes found in directory {0}",
                                  new object[] {strLookupPath});
            }
           
            HbmMapping domainMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();

            // Config accès DB en elle-même.
            var configuration = new Configuration();
            configuration.DataBaseIntegration(c =>
            {
                c.ConnectionString = _strConnectionString;
                c.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
#if debug
                //permet d'afficher le sql généré par nhibernate
                c.LogFormattedSql = true;
                c.LogSqlInConsole = true;
                c.AutoCommentSql = true;
#endif
            });
            configuration.Properties["dialect"] = _strNHibernateDialect;

            // Association Model mapper et configuration
            configuration.AddMapping(domainMapping);

            // Session d'accès à la DB : ready to use.
            _sessionFactory = configuration.BuildSessionFactory();

        }
    }
}
